<template>
	<view id="event-source" :props="mergeProps" :change:props="eventSource.renderPropsChange" v-show="false">
	</view>
</template>

<script>
	export default {
		props: {
			url: {
				type: String,
				default: "",
			},
			options: {
				type: Object,
				default: () => ({}),
			},
		},
		data() {
			return {
				isSend: false,
				isClose: false
			}
		},
		computed: {
			// 合并传入renderjs的数据
			mergeProps({
				url,
				options,
				isSend,
				isClose
			}) {
				return {
					url,
					options,
					isSend,
					isClose
				};
			},
		},
		methods: {
			// 发送
			send() {
				this.isSend = true;
				this.$nextTick(() => {
					this.isSend = false;
				});
			},
			close() {
				this.isClose = true;
				this.$nextTick(() => {
					this.isClose = false;
				});
			},
			// 处理renderjs发回的数据
			emits(e) {
				this.$emit("callback", {
					...e
				});
			},
		},
	};
</script>

<script module="eventSource" lang="renderjs">
	import {fetchEventSource} from '@microsoft/fetch-event-source'
	import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'

	function isAndroid10() {
		const info = plus.os.name === 'Android' ? plus.os.version : ''
		if (info) {
			const majorVersion = parseInt(info.split('.')[0])
			return majorVersion === 10
		}
		return false
	}

	export default {
		data() {
			return {
				abortController: null,
				sse: null
			}
		},
		methods: {
			// 传入数据变更
			renderPropsChange(nVal) {
				const {
					url,
					isSend,
					isClose
				} = nVal || {};
				if (isClose) {
					if (this.abortController) {
						this.abortController.abort()
						this.abortController = null
					}
					if (this.sse) {
						this.sse.close()
						this.sse = null
					}
				}
				if (!isSend) return;
				if (!url) return this.handleEmitData({
					type: "tip",
					msg: "URL cannot be empty."
				});
				this.$nextTick(() => {
					this.handleSSE(nVal);
				});
			},
			// 发送数据到service层
			handleEmitData(data = {}) {
				this.$ownerInstance.callMethod('emits', data);
			},
			// 处理SSE
			handleSSE(opts = {}) {
				const that = this;
				// 检查浏览器是否支持SSE
				if (!('EventSource' in window)) return this.handleEmitData({
					type: "tip",
					msg: "The current device does not support EventSource."
				});
				const {
					url,
					options = {}
				} = opts || {}
				if (isAndroid10()) {
					const body = JSON.parse(options.body)
					const query = []
					for (const key in body) {
						query.push(key + '=' + body[key])
					}
					that.sse = new EventSource(url + '?' + query.join('&'))
					that.sse.onopen = (event) => {
						that.handleEmitData({
							type: "onopen",
							msg: "EventSource onopen."
						})
					}
					that.sse.onmessage = (event) => {
						that.handleEmitData({
							type: "onmessage",
							msg: "EventSource onmessage.",
							data: event.data
						})
					}
					that.sse.onclose = (event) => {
						that.handleEmitData({
							type: "onclose",
							msg: "EventSource onclose."
						});
					}
					that.sse.onerror = (event) => {
						if (event && event.isTrusted) {
							that.sse.close()
							that.sse = null
							that.handleEmitData({
								type: "onclose",
								msg: "EventSource onclose."
							})
						} else {
							that.handleEmitData({
								type: "onerror",
								msg: "EventSource onerror.",
								data: JSON.stringify(event)
							})
						}
					}
					return
				}
				this.abortController = new AbortController()
				options.signal = this.abortController.signal
				fetchEventSource(url, {
					...options,
					onopen() {
						that.handleEmitData({
							type: "onopen",
							msg: "EventSource onopen."
						});
					},
					onmessage(res) {
						that.handleEmitData({
							type: "onmessage",
							msg: "EventSource onmessage.",
							data: res.data
						});
					},
					onclose() {
						that.handleEmitData({
							type: "onclose",
							msg: "EventSource onclose."
						});
					},
					onerror(error) {
						that.handleEmitData({
							type: "onerror",
							msg: "EventSource onerror.",
							data: JSON.stringify(error)
						});
					}
				});
			}
		}
	}
</script>